//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <eltypes.h>
#include <stdlib.h>
#include <ctype.h>
#include "_pubcrt.h"

#ifdef  __cplusplus
extern "C" {
#endif

_ELASTOS_NAMESPACE_USING

const char *icuEncoding[] = {
   NULL,
   "US-ASCII",
   "UTF-7",
   "UTF-8",
   "UTF-16",
   "gb18030",
   "ISO-8859-1",
   "windows-950-2000",
   "ibm-33722_P12A_P12A-2004_U2",
};

Char32 _getAStringCharAt(const char *encoding, const char *str, Int32 index);
Char32 _getWStringCharAt(const wchar_t *str, Int32 index);
Int32 _getAStringIndexOf(const char *encoding, const char *str, Char32 ch);
Int32 _getWStringLastIndexOf(const wchar_t *str, Char32 ch);
Int32 _getAStringLastIndexOf(const char *encoding, const char *str, Char32 ch);
Int32 _getWStringIndexOf(const wchar_t *str, Char32 ch);
Int32 _getAStringLength(const char *encoding, const char *str);
Int32 _getWStringLength(const wchar_t *str);
char* _getAStringSubstring(const char *encoding, const char *str, Int32 start,
            Int32 len, char *substr);
wchar_t* _getWStringSubstring(const wchar_t *str, Int32 start,
            Int32 len, wchar_t *substr);

Int32 __cdecl _WString_GetLength(const wchar_t *wstr, Int32 maxLen)
{
    if (!wstr || maxLen < -1) return -1;
    if (maxLen == 0) return 0;
    if (maxLen == -1)  return wcslen(wstr);

    const wchar_t *p = wstr;
    int i = 0;
    while (*p) {
        if (i >= maxLen) return -1;
        p++;
        i++;
    }
    return (p - wstr);
}

Int32 __cdecl _AString_GetLength(const char *str, Int32 maxLen)
{
    if (!str || maxLen < -1) return -1;
    if (maxLen == 0) return 0;
    if (maxLen == -1)  return strlen(str);

    const char *p = str;
    int i = 0;
    while (*p) {
        if (i >= maxLen) return -1;
        p++;
        i++;
    }
    return (p - str);
}

Int32 __cdecl _WString_EstimateAStringLength(const wchar_t *wstr, Encoding encoding)
{
    if (!wstr) return -1;

    Int32 insize = wcslen(wstr) + 1;
    Int32 outsize = insize * 4;
    return _fromWString(icuEncoding[encoding], NULL, (Int32 *)&outsize,
            wstr, (Int32 *)&insize);
}

Int32 __cdecl _AString_EstimateWStringLength(const char *str, Encoding encoding)
{
    if (!str) return -1;

    Int32 insize = strlen(str) + 1;
    Int32 outsize = insize;
    return _toWString(icuEncoding[encoding], NULL, (Int32 *)&outsize,
                str, (Int32 *)&insize);
}

Int32 __cdecl _WString_Compare(const wchar_t *string1, const wchar_t *string2,
    StringCase stringCase)
{
    if (string1 == string2) return 0;
    if (!string1) return -2;
    if (!string2) return 2;

    if (stringCase == StringCase_Sensitive) {
        return wcscmp(string1, string2);
    }
    else {
        return _wcsicmp(string1, string2);
    }
}

Int32 __cdecl _AString_Compare(const char *string1, const char *string2,
    StringCase stringCase)
{
    if (string1 == string2) return 0;
    if (!string1) return -2;
    if (!string2) return 2;

    if (stringCase == StringCase_Sensitive) {
        return strcmp(string1, string2);
    }
    else {
        return _stricmp(string1, string2);
    }
}

wchar_t* __cdecl _WString_Duplicate(const wchar_t *strSource)
{
    size_t len;
    wchar_t * copy;

    if (!strSource) {
        return NULL;
    }

    len = wcslen(strSource) + 1;
    len *= sizeof(wchar_t);
    if ((copy = (wchar_t *)malloc(len)) == NULL) return NULL;
    memcpy(copy, strSource, len);
    return(copy);
}

void __cdecl _WString_Free(wchar_t * str)
{
    if (str) {
        free((void *)str);
    }
}

char* __cdecl _AString_Duplicate(const char *strSource)
{
    size_t len;
    char * copy;

    if (!strSource) {
        return NULL;
    }

    len = strlen(strSource) + 1;
    if ((copy = (char *)malloc(len)) == NULL) return NULL;
    memcpy(copy, strSource, len);
    return(copy);
}

void __cdecl _AString_Free(char * str)
{
	if (str) {
	    free((void *)str);
	}
}

//---------
Int32 __cdecl _WString_ToInt32(const wchar_t *string)
{
    if (!string) return 0;
    return _wtoi(string);
}

Int32 __cdecl _AString_ToInt32(const char *string)
{
    if (!string) return 0;
    return atoi(string);
}

Int64 __cdecl _WString_ToInt64(const wchar_t *string)
{
    if (!string) return 0;
    return _wtoi64(string);
}

Int64 __cdecl _AString_ToInt64(const char *string)
{
    return _atoi64(string);
}

Boolean __cdecl _WString_ToBoolean(const wchar_t *string)
{
    if (!string) return FALSE;
    if (wcscmp(string, L"1") == 0 || _wcsicmp(string, L"true") == 0) {
        return TRUE;
    }
    else {
        return FALSE;
    }
}

Boolean __cdecl _AString_ToBoolean(const char *string)
{
    if (!string) return FALSE;
    if (strcmp(string, "1") == 0 || _stricmp(string, "true") == 0) {
        return TRUE;
    }
    else {
        return FALSE;
    }
}

Double __cdecl _WString_ToDouble(const wchar_t *string)
{
    if (!string) return 0.0;
    return wcstod(string, NULL);
}

Double __cdecl _AString_ToDouble(const char *string)
{
    if (!string) return 0.0;
    return strtod(string, NULL);
}

//---------
Int32 __cdecl _WString_GetCharCount(const wchar_t *string)
{
    if (!string) return -1;
    return _getWStringLength(string);
}

Int32 __cdecl _AString_GetCharCount(const char *string, Encoding encoding)
{
    if (!string) return -1;
    return _getAStringLength(icuEncoding[encoding], string);
}

Char32 __cdecl _WString_GetChar(const wchar_t *string, Int32 index)
{
    if (!string) return 0xFFFFFFFF;
    if ((index < 0) || (index >= (Int32)wcslen(string))) {
        return 0xFFFFFFFF;
    }

    return _getWStringCharAt(string, index);
}

Char32 __cdecl _AString_GetChar(const char *string, Int32 index,
            Encoding encoding)
{
    if (!string) return 0xFFFFFFFF;
    if ((index < 0) || (index >= (Int32)strlen(string))) {
        return 0xFFFFFFFF;
    }

    return _getAStringCharAt(icuEncoding[encoding], string, index);
}

//---- Contains ----
Boolean __cdecl _AString_Contains(const char *string, const char *substr,
            StringCase stringCase, Encoding encoding)
{
    if (!string || !substr) return FALSE;

    return !!strstr(string, substr);
}

Boolean __cdecl _WString_Contains(const wchar_t *string, const wchar_t *substr,
            StringCase stringCase)
{
    if (!string || !substr) return FALSE;

    return !!wcsstr(string, substr);
}

//---- StartWith ----
Boolean __cdecl _AString_StartWith(const char *string, const char *substr,
            StringCase stringCase, Encoding encoding)
{
    if (!string || !substr) return FALSE;
    if (substr[0] == '\0') return TRUE;

    if (stringCase == StringCase_Sensitive) {
        return !strncmp(string, substr, strlen(substr));
    }
    else {
        return !_strnicmp(string, substr, strlen(substr));
    }
}

Boolean __cdecl _WString_StartWith(const wchar_t *string, const wchar_t *substr,
            StringCase stringCase)
{
    if (!string || !substr) return FALSE;
    if (substr[0] == L'\0') return TRUE;

    if (stringCase == StringCase_Sensitive) {
        return !wcsncmp(string, substr, wcslen(substr));
    }
    else {
        return !_wcsnicmp(string, substr, wcslen(substr));
    }
}

//---- EndWith ----
Boolean __cdecl _AString_EndWith(const char *string, const char *substr,
            StringCase stringCase, Encoding encoding)
{
    if (!string || !substr) return FALSE;
    if (substr[0] == '\0') return TRUE;

    const char *endstr = string + strlen(string) - strlen(substr);
    if (endstr < string) return FALSE;

    return _AString_StartWith(endstr, substr, stringCase, encoding);
}

Boolean __cdecl _WString_EndWith(const wchar_t *string, const wchar_t *substr,
            StringCase stringCase)
{
    if (!string || !substr) return FALSE;
    if (substr[0] == L'\0') return TRUE;

    const wchar_t *endstr = string + wcslen(string) - wcslen(substr);
    if (endstr < string) return FALSE;

    return _WString_StartWith(endstr, substr, stringCase);
}

//---- IndexOf ----
Int32 __cdecl _AString_IndexOf_AChar(const char *string, char ch,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    const char *first = strchr(string, ch);
    if (first) return first - string;
    else return -1;
}

Int32 __cdecl _AString_IndexOf_AnyAChar(const char *string, const char* strCharSet,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    const char *ret = strpbrk(string, strCharSet);
    if (ret) return ret - string;
    else return -1;
}

Int32 __cdecl _AString_IndexOf_Char(const char *string, Char32 ch,
            StringCase stringCase, Encoding encoding)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    return _getAStringIndexOf(icuEncoding[encoding], string, ch);
}


Int32 __cdecl _AString_IndexOf_AnyChar(const char *string, Char32 *strCharSet,
            StringCase stringCase, Encoding encoding)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    assert(0);
    return NULL;
}

Int32 __cdecl _AString_IndexOf_Substring(const char *string, const char *substr,
            StringCase stringCase, Encoding encoding)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string || !substr) return -1;

    const char *index = strstr(string, substr);
    return index ? index - string : -1;
}

Int32 __cdecl _WString_IndexOf_WChar(const wchar_t *string, wchar_t ch,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    const wchar_t *first = wcschr(string, ch);
    if (first) return first - string;
    else return -1;
}

Int32 __cdecl _WString_IndexOf_AnyWChar(const wchar_t *string, const wchar_t* strCharSet,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    const wchar_t *ret = wcspbrk(string, strCharSet);
    if (ret) return ret - string;
    else return -1;
}

Int32 __cdecl _WString_IndexOf_Char(const wchar_t *string, Char32 ch,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    return _getWStringIndexOf(string, ch);
}

Int32 __cdecl _WString_IndexOf_AnyChar(const wchar_t *string, Char32 *strCharSet,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;
    assert(0);
    return NULL;
}

Int32 __cdecl _WString_IndexOf_Substring(const wchar_t *string, const wchar_t *substr,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string || !substr) return -1;

    const wchar_t *index = wcsstr(string, substr);
    return  index ? index - string : -1;
}

//---- LastIndexOf ----
Int32 __cdecl _AString_LastIndexOf_AChar(const char *string, char ch,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    const char *last = strrchr(string, ch);
    if (last) return last - string;
    else return -1;
}

Int32 __cdecl _AString_LastIndexOf_AnyAChar(const char *string, const char* strCharSet,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;
    assert(0);
    return NULL;
}

Int32 __cdecl _AString_LastIndexOf_Char(const char *string, Char32 ch,
            StringCase stringCase, Encoding encoding)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;

    return _getAStringLastIndexOf(icuEncoding[encoding], string, ch);
}

Int32 __cdecl _AString_LastIndexOf_AnyChar(const char *string, Char32 *strCharSet,
            StringCase stringCase, Encoding encoding)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;
    assert(0);
    return NULL;
}

Int32 __cdecl _AString_LastIndexOf_Substring(const char *string, const char *substr,
            StringCase stringCase, Encoding encoding)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string || !substr) return -1;

    if (substr[0] == '\0') {
        int len = strlen(string);
        return len ? len - 1 : 0;
    }

    char *index = strstr(string, substr);
    if (!index) return -1;

    char *last;
    do {
    	last = index;
    	index = strstr(last + 1, substr);
    } while(index);

    return last - string;
}

Int32 __cdecl _WString_LastIndexOf_WChar(const wchar_t *string, wchar_t ch, StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);
    if (!string) return -1;
    const wchar_t *last = wcsrchr(string, ch);
    if (last) return last - string;
    else return -1;
}

Int32 __cdecl _WString_LastIndexOf_AnyWChar(const wchar_t *string, const wchar_t *strCharSet,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;
    assert(0);
    return NULL;
}

Int32 __cdecl _WString_LastIndexOf_Char(const wchar_t *string, Char32 ch,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;
    return _getWStringLastIndexOf(string, ch);
}

Int32 __cdecl _WString_LastIndexOf_AnyChar(const wchar_t *string, Char32 *strCharSet,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string) return -1;
    assert(0);
    return NULL;
}

Int32 __cdecl _WString_LastIndexOf_Substring(const wchar_t *string, const wchar_t *substr,
            StringCase stringCase)
{
    assert(stringCase == StringCase_Sensitive);

    if (!string || !substr) return -1;

    if (substr[0] == L'\0') {
        int len = wcslen(string);
        return len ? len - 1 : 0;
    }

    wchar_t *index = wcsstr(string, substr);
    if (!index) return -1;

    wchar_t *last;
    do {
    	last = index;
    	index = wcsstr(last + 1, substr);
    } while(index);

    return last - string;
}

//---- Substring ----
char* __cdecl _AString_Substring_Buffer(const char *string, Int32 start, PCarQuintet pCq,
            Encoding encoding)
{
    if (!string || !pCq || !pCq->m_pBuf || start < 0) return NULL;

	Int32 nlength = (int)strlen(string);
	if (start > nlength) return NULL;

    Int32 len = MIN(nlength - start + 1, pCq->m_size - 1);
    _getAStringSubstring(icuEncoding[encoding], string, start, len,
                (char *)pCq->m_pBuf);
    pCq->m_used = strlen((char *)pCq->m_pBuf) + 1;
    return  (char *)pCq->m_pBuf;
}

char* __cdecl _AString_Substring(const char *string, Int32 start, Encoding encoding)
{
    if (!string || start < 0 || start > (int)strlen(string)) return NULL;

    return _getAStringSubstring(icuEncoding[encoding], string, start, -1, NULL);
}

char* __cdecl _AString_Substring_Length_Buffer(const char *string, Int32 start, Int32 len,
    PCarQuintet pCq, Encoding encoding)
{
    if (!string || !pCq || !pCq->m_pBuf || start < 0 || len < 0) return NULL;

	Int32 nlength = (int)strlen(string);
	if (start > nlength) return NULL;

    len = MIN(nlength - start + 1, len);
    len = MIN(len, pCq->m_size - 1);
    _getAStringSubstring(icuEncoding[encoding], string, start, len,
                (char *)pCq->m_pBuf);
    pCq->m_used = strlen((char *)pCq->m_pBuf) + 1;
    return  (char *)pCq->m_pBuf;
}

wchar_t* __cdecl _WString_Substring_Buffer(const wchar_t *string, Int32 start,
    PCarQuintet pCq)
{
    if (!string || !pCq || !pCq->m_pBuf || start < 0) return NULL;

    UInt32 nlength = wcslen(string);
	if (start > (Int32)nlength) return NULL;

    Int32 len = MIN(nlength - start + 1, pCq->m_size / sizeof(wchar_t) - 1);
    _getWStringSubstring(string, start, len, (wchar_t *)pCq->m_pBuf);
    pCq->m_used = (wcslen((wchar_t *)pCq->m_pBuf) + 1) * sizeof(wchar_t);
    return  (wchar_t *)pCq->m_pBuf;
}

wchar_t* __cdecl _WString_Substring(const wchar_t *string, Int32 start)
{
    if (!string || start < 0 || start > (int)wcslen(string)) return NULL;

    return _getWStringSubstring(string, start, -1, NULL);
}

wchar_t* __cdecl _WString_Substring_Length_Buffer(const wchar_t *string, Int32 start, Int32 len,
            PCarQuintet pCq)
{
    if (!string || !pCq || !pCq->m_pBuf || start < 0 || len < 0) return NULL;

    Int32 nlength = (int)wcslen(string);
	if (start > nlength) return NULL;

    len = MIN(nlength - start + 1, len);
    len = MIN(len, (int)(pCq->m_size / sizeof(wchar_t)) - 1);
    _getWStringSubstring(string, start, len, (wchar_t *)pCq->m_pBuf);
    pCq->m_used = (wcslen((wchar_t *)pCq->m_pBuf) + 1) * sizeof(wchar_t);
    return  (wchar_t *)pCq->m_pBuf;
}

//---- ToLowerCase ----
char* __cdecl _AString_ToLowerCase(const char *string, PCarQuintet pCq,
            Encoding encoding)
{
    if (!string || !pCq || !pCq->m_pBuf) return NULL;
    _AStringBuf_Copy(pCq, string);

    _strlwr((char *)pCq->m_pBuf);
    return (char *)pCq->m_pBuf;
}

wchar_t* __cdecl _WString_ToLowerCase(const wchar_t *string, PCarQuintet pCq)
{
    if (!string || !pCq || !pCq->m_pBuf) return NULL;
    _WStringBuf_Copy(pCq, string);
    _wcslwr((wchar_t *)pCq->m_pBuf);
    return (wchar_t *)pCq->m_pBuf;
}

//---- ToUpperCase ----
char* __cdecl _AString_ToUpperCase(const char *string, PCarQuintet pCq,
            Encoding encoding)
{
    if (!string || !pCq || !pCq->m_pBuf) return NULL;
    _AStringBuf_Copy(pCq, string);
    _strupr((char *)pCq->m_pBuf);
    return (char *)pCq->m_pBuf;
}

wchar_t* __cdecl _WString_ToUpperCase(const wchar_t *string, PCarQuintet pCq)
{
    if (!string || !pCq || !pCq->m_pBuf) return NULL;
    _WStringBuf_Copy(pCq, string);
    _wcsupr((wchar_t *)pCq->m_pBuf);
    return (wchar_t *)pCq->m_pBuf;
}

//---- TrimStart ----
char* __cdecl _AString_TrimStart(const char *string, PCarQuintet pCq)
{
    if (!string || !pCq || !pCq->m_pBuf) return NULL;

    while (isspace(*string) && *string) {
        string++;
    }

    _AStringBuf_Copy(pCq, string);

    return (char *)pCq->m_pBuf;
}

wchar_t* __cdecl _WString_TrimStart(const wchar_t *string, PCarQuintet pCq)
{
    if (!string || !pCq || !pCq->m_pBuf) return NULL;

    while (isspace(*string) && *string) {
        string++;
    }

    _WStringBuf_Copy(pCq, string);

    return (wchar_t *)pCq->m_pBuf;
}

//---- TrimEnd ----
char* __cdecl _AString_TrimEnd(const char *string, PCarQuintet pCq)
{
    if (!string || !pCq || !pCq->m_pBuf) return NULL;

    const char* pend = string + strlen(string) - 1;
    while (isspace(*pend) && pend >= string) {
        pend--;
    }

    Int32 nLen = (pend - string + 1) * sizeof(AChar);
    Int32 nCap = pCq->m_size - sizeof(AChar);
    nLen = MIN(nLen, nCap);

    memcpy(pCq->m_pBuf, string, nLen);
    *(AChar *)((Byte *)pCq->m_pBuf + nLen) = '\0';
    pCq->m_used = nLen + sizeof(AChar);

    return (char *)pCq->m_pBuf;
}

wchar_t* __cdecl _WString_TrimEnd(const wchar_t *string, PCarQuintet pCq)
{
    if (!string || !pCq || !pCq->m_pBuf) return NULL;

    const wchar_t* pend = string + wcslen(string) - 1;
    while (isspace(*pend) && pend >= string) {
        pend--;
    }

    Int32 nLen = (pend - string + 1) * sizeof(WChar);
    Int32 nCap = pCq->m_size - sizeof(WChar);
    nLen = MIN(nLen, nCap);

    memcpy(pCq->m_pBuf, string, nLen);
    *(WChar *)((Byte *)pCq->m_pBuf + nLen) = L'\0';
    pCq->m_used = nLen + sizeof(WChar);

    return (wchar_t *)pCq->m_pBuf;
}

//---- Trim ----
char* __cdecl _AString_Trim(const char *string, PCarQuintet pCq)
{
    return _AString_TrimEnd(_AString_TrimStart(string, pCq), pCq);
}

wchar_t* __cdecl _WString_Trim(const wchar_t *string, PCarQuintet pCq)
{
    return _WString_TrimEnd(_WString_TrimStart(string, pCq), pCq);
}

//AStringBuf& AStringBuf::Trim(AString encoding = NULL);
//
//WStringBuf& WStringBuf::Trim();

#ifdef  __cplusplus
}
#endif
